package com.xd.controll;

import com.xd.model.CandidateEdge;
import com.xd.model.CandidatePoint;
import com.xd.model.GPSPoint;
import com.xd.util.CommonCalculationUtil;
import com.xd.util.DataInputUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;

/**
 * designed by Matt
 *
 * @author Matt
 *         e-mail:ZHUHAN1401@126.com
 * @version JDK 1.8.0_101
 * @since 2017/6/10 13:25 星期六
 * Description: description
 */
public class STMatching {

    private static DataInputUtil dataInputUtil;

    public void setDataInputUtil(DataInputUtil dataInputUtil){
        this.dataInputUtil = dataInputUtil;
    }

    /**
     * 导入并初始化候选点，根据所属GPSPoint分组
     *
     * @param GPSPoints
     */
    public LinkedList<GPSPoint> candidatePreparation(LinkedList<GPSPoint> GPSPoints) {
        //导入候选点
        LinkedList<CandidatePoint> candidatePoints = dataInputUtil.getCandidatePoints();
        //计算正态分布，flag设false，为gps点添加其所对应的候选点
        for (CandidatePoint point : candidatePoints) {
            Double gass=CommonCalculationUtil.GaussDistribution(point.getNearDist());
            point.setGaussDistribution(gass);
            point.setFlag(false);
            Long infID = point.getInfID();
            for (GPSPoint gpsPoint : GPSPoints) {
                if (gpsPoint.getObjectID().equals(infID)) {
                    gpsPoint.addCandidatePoint(point);
                }
            }
        }
        return GPSPoints;
    }

    /**
     * 计算转移概率、余弦距离、各边f值并保存到构建的候选边当中
     *
     * @param GPSPoints
     */
    public LinkedList<GPSPoint> calaTransProConstructEdge(LinkedList<GPSPoint> GPSPoints) {
        LinkedList<HashMap<String, CandidateEdge>> candidateEdgeGraph = new LinkedList<HashMap<String, CandidateEdge>>();
        //添加空白头节点,第2个节点下标为1，则从1开始遍历
        //candidateEdgeGraph.add(null);

        //size-1=3  只需要前面3个
        for (Integer i = 0; i < GPSPoints.size() - 1; i++) {
            GPSPoint p1 = GPSPoints.get(i);
            GPSPoint p2 = GPSPoints.get(i + 1);
            //获取gps点距离和以秒为单位的时间差
            Double disOfGPS = CommonCalculationUtil.calaDistanceInNearXY(p1.getNearX(), p2.getNearX(), p1.getNearY(), p2.getNearY());

            Long timeInerval = (p1.getpTime().getTime() - p2.getpTime().getTime());
            HashMap<String, CandidateEdge> edges = new HashMap<String, CandidateEdge>();
            for (CandidatePoint candidatePointA : p1.getCandidatePoints()) {
                for (CandidatePoint candidatePointB : p2.getCandidatePoints()) {
                    CandidateEdge edge = new CandidateEdge(candidatePointA, candidatePointB);

                    //获取候选点距离,计算转移概率
                    Double disOfCandi = dataInputUtil.findShortestPathLength(candidatePointA.getObjectID(), candidatePointB.getObjectID());
                    edge.setTransPro(disOfGPS / disOfCandi);
                    //计算平均速度
                    Double aveSpeed = disOfCandi / timeInerval;
                    //获取最短路径上的道路类型，计算余弦距离
                    LinkedList<String> type = dataInputUtil.findShortestPathType(candidatePointA.getObjectID(), candidatePointB.getObjectID());
                    Double cosDis=CommonCalculationUtil.cosineDistance(type, aveSpeed);
                    edge.setTemporalValue(cosDis);

                    //保存选择前的fValue，不一定是最大的
                    Double fValue=(edge.geteEnd().getGaussDistribution() * edge.getTransPro() * edge.getTemporalValue()) + edge.geteStart().getGaussDistribution();

                    edge.setfValue(fValue);
                    candidatePointB.getTemFValue().add(fValue);

                    edges.put(candidatePointA.getObjectID() +"."+ candidatePointB.getObjectID() + "", edge);
                }
            }
            candidateEdgeGraph.add(edges);
        }
        return GPSPoints;
    }

    public LinkedList<CandidatePoint> resultMapMatching(LinkedList<GPSPoint> GPSPoints) {
        LinkedList<CandidatePoint> finalPath=new LinkedList<CandidatePoint>();

        // 初始化STValuesa,为第一个GPS点的候选点C赋f值为正态分布值
        GPSPoint firstPoint = GPSPoints.getFirst();
        HashMap<CandidatePoint, Double> firstSTValues = new HashMap<CandidatePoint, Double>();
        for (CandidatePoint candidatePoint : firstPoint.getCandidatePoints()) {
            firstSTValues.put(candidatePoint, candidatePoint.getGaussDistribution());
        }
        firstPoint.setPointsSTValues(firstSTValues);

        // 计算其余GPS点的候选点的f值
        for (Integer i = 1; i < GPSPoints.size(); i++) {
            GPSPoint gpsPoint = GPSPoints.get(i);
            // 每一个GPS的f表格
            HashMap<CandidatePoint, Double> STValuesMap = new HashMap<CandidatePoint, Double>();
            // 处理GPS点的候选点
            for (CandidatePoint candidatePoint : gpsPoint.getCandidatePoints()) {
                Double candidatePointfValue=CommonCalculationUtil.findMaxVal(candidatePoint.getTemFValue());
                STValuesMap.put(candidatePoint,candidatePointfValue);
            }
            gpsPoint.setPointsSTValues(STValuesMap);
        }
        // 根据每个GPS点的f值计算结果，选择概率最大的候选点
        for (GPSPoint point:GPSPoints){
            CandidatePoint candidatePoint=CommonCalculationUtil.findMaxVal(point.getPointsSTValues());
            if (candidatePoint!=null){
                candidatePoint.setFlag(true);
                finalPath.add(candidatePoint);
            }
        }
        return finalPath;
    }

    public static void main(String[] args) {
        //初始化GPS点，设置时间，XY坐标
        GPSPoint point16 = new GPSPoint(16l, 453791, 4415759, new Date(1498406972l));
        GPSPoint point18 = new GPSPoint(18l, 453878, 4415455, new Date(1498407008l));
        GPSPoint point19 = new GPSPoint(19l, 454267, 4415482, new Date(1498407048l));
        GPSPoint point20 = new GPSPoint(20l, 454283, 4416422, new Date(1498407148l));


        /*
        * 1226432043l   1
        * 1226432113l   2
        * 1226432188l   3
        * 1226432263l   4
        * 1226432343l   5
        *
        GPSPoint point1 = new GPSPoint(20l, 454283, 4416422, new Date(1226432043l));
        GPSPoint point2 = new GPSPoint(20l, 454283, 4416422, new Date(1226432113l));
        GPSPoint point3 = new GPSPoint(20l, 454283, 4416422, new Date(1226432188l));
        GPSPoint point4 = new GPSPoint(20l, 454283, 4416422, new Date(1226432263l));
        GPSPoint point5 = new GPSPoint(20l, 454283, 4416422, new Date(1226432343l));
        */

        LinkedList<GPSPoint> gpsPoints = new LinkedList<GPSPoint>();
        gpsPoints.add(point16);
        gpsPoints.add(point18);
        gpsPoints.add(point19);
        gpsPoints.add(point20);

        ApplicationContext context= new ClassPathXmlApplicationContext("classpath:spring/spring-dao.xml");
        STMatching stMatching=(STMatching) context.getBean("STMatching");

        gpsPoints =stMatching.candidatePreparation(gpsPoints);
        gpsPoints =stMatching.calaTransProConstructEdge(gpsPoints);
        LinkedList<CandidatePoint> pointList =stMatching.resultMapMatching(gpsPoints);
        for (CandidatePoint cPoint:pointList){
            System.out.println(cPoint);
        }
    }
}
